package com.walker.component.page.resolve.factory;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

import org.codehaus.jackson.map.ObjectMapper;

import com.walker.component.page.resolve.exception.JsonException;
import com.walker.component.page.resolve.exception.PageResolveException;
import com.walker.component.page.resolve.query.DateQueryer;
import com.walker.component.page.resolve.query.IntQueryer;
import com.walker.component.page.resolve.query.ListQueryer;
import com.walker.component.page.resolve.query.ObjectQueryer;
import com.walker.component.page.resolve.query.Queryer;
import com.walker.component.page.resolve.query.StringQueryer;
import com.walker.component.page.resolve.query.annotation.Required;

/**
 * 查询器工厂类
 * 
 * @author 三丰
 * 
 */
public class JsonQueryerFactory {
	/**
	 * 查询器类型map
	 */
	private static Map<String, Class<? extends Queryer>> namedQueryers = new HashMap();
	private static ObjectMapper objectMapper;

	static {
		namedQueryers.put("string", StringQueryer.class);
		namedQueryers.put("int", IntQueryer.class);
		namedQueryers.put("list", ListQueryer.class);
		namedQueryers.put("date", DateQueryer.class);

		objectMapper = new ObjectMapper();
	}

	private static Object reBuildMap(Object expr) {
		Map configuration = (Map) expr;
		Object queryExpr = configuration.get("query_exp");
		return queryExpr;
	}

	public static Queryer makeQueryer(InputStream json) throws JsonException, PageResolveException {
		Object queryExpr;
		try {
			queryExpr = reBuildMap(objectMapper.readValue(json, Object.class));
		} catch (Exception e) {
			throw new JsonException(e);
		}
		return makeQueryer(queryExpr);
	}

	public static Queryer makeQueryer(String json) throws JsonException, PageResolveException {
		Object queryExpr;
		try {
			queryExpr = reBuildMap(objectMapper.readValue(json, Object.class));
		} catch (Exception e) {
			throw new JsonException(e);
		}
		return makeQueryer(queryExpr);
	}

	public static Queryer makeQueryer(Object queryExpr) throws PageResolveException {
		if (!(queryExpr instanceof Map)) {
			throw new PageResolveException("Illegal PageResolve expression: " + queryExpr);
		}
		Map queryExprMap = (Map) queryExpr;
		if (queryExprMap.containsKey("_type")) {
			return makeComplexQueryer(queryExprMap);
		}
		return makeObjectQueryer(queryExprMap);

	}

	private static Queryer makeComplexQueryer(Map<String, Object> queryExpr) {
		String type;
		try {
			type = (String) queryExpr.get("_type");
		} catch (ClassCastException e) {
			throw new PageResolveException("'_type' field must be a string.");
		}
		if (type == null) {
			throw new PageResolveException("Complexed queryers must contain a '_type' field.");
		}
		Class queryerClass = namedQueryers.get(type);
		if (queryerClass == null) {
			throw new PageResolveException("Unsupported queryer type: '" + type + "'.");
		}
		Queryer queryer;
		try {
			queryer = (Queryer) queryerClass.newInstance();
		} catch (Exception e) {
			throw new PageResolveException("Cannot instantiate queryer '" + type + "'.", e);
		}
		Map queryExprCopy = new HashMap(queryExpr);
		queryExprCopy.remove("_type");
		PropertyDescriptor[] propertyDescriptors;
		try {
			BeanInfo beanInfo = Introspector.getBeanInfo(queryerClass);
			propertyDescriptors = beanInfo.getPropertyDescriptors();
		} catch (IntrospectionException e) {
			throw new PageResolveException("Cannot introspect queryer '" + type + "'.", e);
		}

		for (PropertyDescriptor pd : propertyDescriptors) {
			String propertyName = pd.getName();
			// System.out.println("propertyName=" + propertyName);
			Method writeMethod = pd.getWriteMethod();
			if (writeMethod == null) {
				continue;
			}
			Object exprValue = queryExpr.get(propertyName);
			if (exprValue != null) {
				Class propertyType = pd.getPropertyType();
				Object valueToWrite;
				if (Queryer.class.isAssignableFrom(propertyType)) {
					valueToWrite = makeQueryer(exprValue);
				} else {
					valueToWrite = exprValue;
				}
				try {
					writeMethod.invoke(queryer, new Object[] { valueToWrite });
				} catch (Exception e) {
					throw new PageResolveException("Cannot set property '" + propertyName + "' on Queryer type '" + type + "'.", e);
				}
				queryExprCopy.remove(propertyName);
			} else {
				Required requiredAnnotation = writeMethod.getAnnotation(Required.class);
				if (requiredAnnotation != null) {
					throw new PageResolveException("Property '" + propertyName + "' is required for Queryer type '" + type + "'.");
				}
			}
		}
		// 遍历定义错误的属性
		if (!(queryExprCopy.isEmpty())) {
			StringBuilder sb = new StringBuilder();
			int j = 1;
			for (Iterator<String> iterator = queryExprCopy.keySet().iterator(); iterator.hasNext();) {
				String str = iterator.next();
				if (j != 0) {
					j = 0;
				} else {
					sb.append(',');
				}
				sb.append(str);
			}
			throw new PageResolveException("Unexpected property '" + sb.toString() + "' on Queryer type '" + type + "'.");
		}
		return queryer;
	}

	@SuppressWarnings("rawtypes")
	private static Queryer makeObjectQueryer(Map<String, Object> queryExpr) {
		Map fieldRules = new LinkedHashMap();
		for (Map.Entry pair : queryExpr.entrySet()) {
			Queryer fieldQueryer = makeQueryer(pair.getValue());
			fieldRules.put(pair.getKey(), fieldQueryer);
		}
		return new ObjectQueryer(fieldRules);
	}
}